/**
 *  Copyright 23.07.2012 oliver.keusemann@googlemail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package de.xelerity.geolessi.types.poi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.xelerity.geolessi.preferences.Preferences;
import de.xelerity.geolessi.types.mixed.Koordinate;
import de.xelerity.geolessi.types.verbindung.Linie;
import de.xelerity.geolessi.types.verbindung.Linien;
import de.xelerity.geolessi.types.verbindung.Richtung;
import de.xelerity.helper.StringFunktionen;

public class HalteStelle {

	public static final long HalteStelleTyp_BUS = Linie.LinienTyp_BUS;
	public static final long HalteStelleTyp_SBAHN = Linie.LinienTyp_SBAHN;
	public static final long HalteStelleTyp_UBAHN = Linie.LinienTyp_UBAHN;
	public static final long HalteStelleTyp_ZAHNRAD = Linie.LinienTyp_ZAHNRAD;

	// Auch ergaenzen in Klasse 'Linie' und in 'getHalteStellenTypAsString'

	public static final long HalteStelleTyp_ALLE = 0xFFFFFFFF;

	private Koordinate koordinate;
	private String title;

	private String addressbez;

	// Haltestelle kann UBahn- und/oder SBahn- und/oder Bus-Haltestelle sein...
	private long HalteStelleTyp = 0L;

	// Haltestelle hat folgende IDs von verschiedenen Fahrplanauskunftssystemen
	// Haltestelle kann auch mehere IDs vom gleichen Provider haben. z.B. Ruit
	// hat bei DB: 562247 und 562248 (warum auch immer)
	private HashMap<HaltestellenIDProvider, ArrayList<String>> IDs;

	// Haltestelle wird von x Linien angefahren
	private Linien allLinien;

	public HalteStelle() {
		allLinien = new Linien();
		IDs = new HashMap<HaltestellenIDProvider, ArrayList<String>>();
	}

	public void addHaltestellenIDs(HashMap<HaltestellenIDProvider, ArrayList<String>> pIDs) {
		if (pIDs == null) {
			return;
		}

		for (Map.Entry<HaltestellenIDProvider, ArrayList<String>> entry : pIDs.entrySet()) {
			HaltestellenIDProvider IDProvider = entry.getKey();
			ArrayList<String> IDs = entry.getValue();
			if (IDs != null) {
				for (String S : IDs) {
					addHaltestellenID(IDProvider, S);
				}
			}
		}
	}

	public void addHaltestellenID(HaltestellenIDProvider pHaltestellenIDProvider, String pID) {
		if (pID != null && pID.trim().length() > 0 && pHaltestellenIDProvider != null) {
			pID = pID.replaceAll("\\s", "").toLowerCase();

			ArrayList<String> arrayListHSNummern = IDs.get(pHaltestellenIDProvider);
			if (arrayListHSNummern == null) {
				arrayListHSNummern = new ArrayList<String>();
			}

			for (String HSNummer : arrayListHSNummern) {
				if (HSNummer.equalsIgnoreCase(pID)) {
					return;
				}
			}
			arrayListHSNummern.add(pID);
			IDs.put(pHaltestellenIDProvider, arrayListHSNummern);
		}
	}

	public Koordinate getKoordinate() {
		return koordinate;
	}

	public void setKoordinate(Koordinate koordinate) {
		this.koordinate = koordinate;
	}

	public String getTitle() {
		return title == null ? "" : title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public long getHalteStelleTyp() {
		return HalteStelleTyp;
	}

	public String getHalteStelleTypAsString() {
		String S = "";
		if ((HalteStelleTyp & HalteStelleTyp_BUS) > 0L) {
			S = S + "Bus,";
		}
		if ((HalteStelleTyp & HalteStelleTyp_SBAHN) > 0L) {
			S = S + "SBahn,";
		}
		if ((HalteStelleTyp & HalteStelleTyp_UBAHN) > 0L) {
			S = S + "UBahn,";
		}
		if ((HalteStelleTyp & HalteStelleTyp_ZAHNRAD) > 0L) {
			S = S + "ZR,";
		}
		S = S.replaceAll(",$\\s*", "");
		return "(" + S + ")";
	}

	public void setHalteStelleTyp(long halteStelleTyp) {
		HalteStelleTyp = halteStelleTyp;
	}

	public void addHalteStelleTyp(long halteStelleTyp) {
		HalteStelleTyp = HalteStelleTyp | halteStelleTyp;
	}

	public List<String> getIDs(HaltestellenIDProvider pHaltestellenIDProvider) {
		ArrayList<String> arrayList = IDs.get(pHaltestellenIDProvider);
		if (arrayList == null) {
			return new ArrayList<String>();
		} else {
			return arrayList;
		}
	}

	public HashMap<HaltestellenIDProvider, ArrayList<String>> getAllIDs() {
		return IDs == null ? new HashMap<HaltestellenIDProvider, ArrayList<String>>() : IDs;
	}

	public String getIDDescAsString() {
		if (IDs == null) {
			return "<no IDs defined>";
		}

		String res = "";
		for (Map.Entry<HaltestellenIDProvider, ArrayList<String>> entry : IDs.entrySet()) {
			HaltestellenIDProvider IDProvider = entry.getKey();
			ArrayList<String> IDs = entry.getValue();
			Collections.sort(IDs);
			res = res + IDProvider.name() + "=[";
			if (IDs != null) {
				for (String S : IDs) {
					res = res + S + ", ";
				}
			}
			res = res.replaceAll(", $", "") + "]";
		}
		return res;
	}

	public boolean hasID(HaltestellenIDProvider pHaltestellenIDProvider) {
		ArrayList<String> arrayList = IDs.get(pHaltestellenIDProvider);
		if (arrayList == null) {
			return false;
		} else {
			return arrayList.size() > 0;
		}
	}

	public Linien getLinien() {
		return allLinien == null ? new Linien() : allLinien;
	}

	public void setLinien(Linien linien) {
		allLinien = linien;
	}

	public void addLinie(Linie linie) {
		if (linie == null) {
			return;
		}
		if (allLinien == null) {
			allLinien = new Linien();
		}

		long linienTyp = linie.getLinienTyp();
		addHalteStelleTyp(linienTyp);

		// Test ob Linie schon vorhanden
		String LStr = linie.getLinienTypAsString() + linie.getNummer();
		boolean LExists = false;
		Linie existingLine = null;
		for (Linie L : getLinien()) {
			String LThisStr = L.getLinienTypAsString() + L.getNummer();
			if (LStr.equalsIgnoreCase(LThisStr)) {
				LExists = true;
				existingLine = L;
				break;
			}
		}

		if (!LExists || existingLine == null) {
			allLinien.add(linie);
			return;
		}

		// Linie vorhanden. Evtl. Richtungen hinzufuegen
		// Doppelte werden von 'addDirection' geblockt.
		for (Richtung LDir : linie.getDirections()) {
			existingLine.addDirection(LDir);
		}

	}

	/*
	 * public boolean isSBahn() {
	 * return (HalteStelleTyp & HalteStelleTyp_SBAHN) > 0L;
	 * }
	 * 
	 * public boolean isUBahn() {
	 * return (HalteStelleTyp & HalteStelleTyp_UBAHN) > 0L;
	 * }
	 * 
	 * public boolean isBus() {
	 * return (HalteStelleTyp & HalteStelleTyp_BUS) > 0L;
	 * }
	 * 
	 * public boolean isZahnrad() {
	 * return (HalteStelleTyp & HalteStelleTyp_ZAHNRAD) > 0L;
	 * }
	 */

	public String getAddressbez() {
		return addressbez == null ? "" : addressbez;
	}

	public void setAddressbez(String addressbez) {
		this.addressbez = addressbez;
	}

	public int hashCodeL1() {
		String res = "";

		List<Integer> list = new ArrayList<Integer>();

		res = getTitle().replaceAll("[^0-9a-zA-Z]", "");
		if (StringFunktionen.isEmptyTrimed(res)) {
			res = "L";
		}
		list.add(res.toLowerCase().hashCode());

		res = getAddressbez().replaceAll("[^a-zA-Z]", "");
		if (StringFunktionen.isEmptyTrimed(res)) {
			res = "M";
		}
		list.add(res.toLowerCase().hashCode());

		Linien linien = getLinien();

		if (linien == null || linien.size() <= 0) {
			list.add("N".hashCode());
		} else {
			for (Linie L : linien) {
				res = L.getLinienTypAsString() + L.getNummer();
				for (Richtung dirs : L.getDirections()) {
					res = res + dirs.getDirection();
				}
				res = res.replaceAll("[^0-9a-zA-Z]", "");
				list.add(res.toLowerCase().hashCode());
			}
		}

		Collections.sort(list);
		res = "";
		for (Integer I : list) {
			res = res + (I.intValue() + "");
		}

		return res.hashCode();
	}

	// Linie.LinienTyp_BUS

	public boolean moveLinien(HalteStelle pHS, long LinenTyp) {
		if (pHS == null) {
			return false;
		}
		boolean hasMoved = false;
		long halteStelleTyp2 = pHS.getHalteStelleTyp();

		halteStelleTyp2 &= ~(LinenTyp);

		boolean move = true;
		while (move) {
			move = false;
			for (int i = 0; i < pHS.getLinien().size(); i++) {
				Linie LNeu = pHS.getLinien().get(i);
				if (LNeu.getLinienTyp() != LinenTyp) {
					continue;
				}

				addLinie(LNeu);
				pHS.getLinien().remove(i);
				move = true;
				pHS.setHalteStelleTyp(halteStelleTyp2);
				addHaltestellenIDs(pHS.getAllIDs());
				hasMoved = true;
			}
		}
		return hasMoved;
	}

	public void merge(HalteStelle pHS) {
		if (pHS == null) {
			return;
		}

		// Merge Coord
		if (Preferences.DOHSMERGEMITTELCOORD) {
			setKoordinate(Koordinate.mittel(pHS.getKoordinate(), getKoordinate()));
		}

		// Merge IDs
		HashMap<HaltestellenIDProvider, ArrayList<String>> allIDs = pHS.getAllIDs();
		for (Map.Entry<HaltestellenIDProvider, ArrayList<String>> entry : allIDs.entrySet()) {
			for (String S : entry.getValue()) {
				addHaltestellenID(entry.getKey(), S);
			}
		}

		// Merge Linien + Richtungen;
		for (Linie LNeu : pHS.getLinien()) {
			boolean LExists = false;
			for (Linie LAlt : getLinien()) {
				if (LAlt.equals(LNeu)) {
					LExists = true;
					break;
				}
			}

			if (!LExists) {
				addLinie(LNeu);
			}
		}

		// Merge Title und Adresse
		if (!StringFunktionen.isEmptyTrimed(pHS.getAddressbez()) && StringFunktionen.isEmptyTrimed(getAddressbez())) {
			setAddressbez(pHS.getAddressbez());
		}

	}

}
